package cn.cyj.security.security;

import cn.cyj.security.ValidateCode.ValidateCodeFilter;
import cn.cyj.security.handler.MyAuthenticationFailureHandler;
import cn.cyj.security.handler.MyAuthenticationSucessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationSucessHandler authenticationSucessHandler;
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Autowired
    private ValidateCodeFilter validateCodeFilter;
    @Autowired
    private UserDetailService userDetailService;
    @Autowired
    private DataSource dataSource;

    /**
     * token持久化对象
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }

    /**
     * 配置密码加密类 security自带的bcrypt加密器足够强大,同样密码两次加密各不相同
     * 加密前-->123456  加密后-->$2a$10$wTFarQqjRCa.X7VptPzJpuETSmBbfFCcZxBeTWiZbvnBYfkCLSRr.
     * 加密前-->123456  加密后-->$2a$10$0P3cGIO29C5r5RAq2Lf2hu4W1luTET5IizN0LY.1lk44ukcpfp/8q
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 链式调用步骤解释
         * 首先先执行验证码过滤器.
         * 使用表单登录,解析路径后缀的控制器来决定跳转,对应form中的action地址为/login,登录成功后交给处理器觉得跳转页面
         * 并且
         * 使用记住我功能,设置token对象,保存时间设置,设置完成自动登录的接口
         * 并且
         * 开启session管理,党session失效时跳转到指定url
         * 账户支持同时登录个数设置, 设置session失效后的策略
         * (除了踢出当前用户来保证当前登录唯一,还可使用maxSessionsPreventsLogin(true)来限制相同用户再次登录)
         * 开启授权配置,除了指定资源之外的全部请求都需要认证
         * 并且
         * 关闭csrf防御
         */
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin().loginPage("/authentication/require").loginProcessingUrl("/login")
                .successHandler(authenticationSucessHandler)
                .failureHandler(myAuthenticationFailureHandler)
                .and()
                .rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(300).userDetailsService(userDetailService)
                .and()
                .authorizeRequests().antMatchers("/authentication/require", "/css/*", "/login.html", "/code/image", "/code/sms", "/login/mobile").permitAll().anyRequest().authenticated()
                .and().sessionManagement().invalidSessionUrl("/session/invalid")
                .maximumSessions(1).expiredSessionStrategy(null)
                .and()
                .and().csrf().disable();

    }
}
